﻿using Core.Framework;
using Core.Framework.Common;
using DotNetty.Framework.BaseModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace DotNetty.Framework.BaseTriggers
{
    public abstract class AbstractHttpTrigger
    {
        protected readonly IList<IHttpWorker> HttpWorkers = new List<IHttpWorker>();
        protected readonly IList<HttpWorkerInfo> httpWorkerInfos = new List<HttpWorkerInfo>();
        protected readonly Dictionary<string, RouteTemplate> routeTemplates = new Dictionary<string, RouteTemplate>();
        protected IUrlPathToUrlTemplateMatcher matcher { get; }
        public AbstractHttpTrigger()
        {
            this.matcher = new RegExUrlMatcher();
            this.INIHttpController();
        }

        public (HttpWorkerInfo worker, List<PlaceholderNameAndValue> parameters) GetApiWorker<THttpContext>(THttpContext context)
            where THttpContext : AbstractHttpContext, new()
        {

            Console.WriteLine("GetApiWorker:{0},{1}", context.Path, context.QueryString);

            foreach (var worker in this.httpWorkerInfos)
            {
                if (context.Method.Name == worker.MethodType)
                {
                    if (this.matcher.Match(context.Path, string.Empty, routeTemplates[worker.RouteTemplate]))
                    {
                        IUrlPathPlaceholderNameAndValueFinder placeholder = new UrlPathPlaceholderNameAndValueFinder();
                        var parameters = placeholder.Find(context.Path, context.QueryString, worker.RouteTemplate);
                        return (worker, parameters);
                    }
                }
            }

            return default;
        }

        void INIHttpController()
        {
            var worker = AssemblyUtils.GetTypesByAttribute<Route>();
            IRouteTemplatePatternCreator routeTemplatePatternCreator = new RouteTemplatePatternCreator();

            foreach (var item in worker)
            {
                var routeAttr = item.GetCustomAttribute<Route>();

                string routeTemp = routeAttr?.Template;

                if (!routeTemp.StartsWith("/"))
                {
                    routeTemp = "/" + routeTemp;
                }

                string name = string.Empty;
                if (item.Name.EndsWith("Worker"))
                {
                    name = item.Name.Substring(0, item.Name.Length - 6);
                }

                routeTemp = string.IsNullOrWhiteSpace(routeTemp) ? name : routeTemp.Replace("{worker}", name);

                var instance = AssemblyUtils.CreateInstanceByType(item, type => ProviderOfServices.GetService(type, "HttpWorker"));

                var methods = item.GetMethods();
                foreach (var method in methods)
                {
                    var methodAttrs = method.GetCustomAttributes();

                    if (methodAttrs.Count() > 0)
                    {
                        foreach (var attr in methodAttrs)
                        {

                            HttpWorkerInfo httpWorkerInfo = null;

                            switch (attr)
                            {
                                case var a when a is Get get:
                                    httpWorkerInfo = new HttpWorkerInfo
                                    {
                                        MethodType = "GET",
                                        RouteTemplate = string.IsNullOrWhiteSpace(get.Template) ? $"{routeTemp}/{method.Name}" : $"{routeTemp}/{get.Template}"
                                    };
                                    break;
                                case var a when a is Post post:
                                    httpWorkerInfo = new HttpWorkerInfo
                                    {
                                        MethodType = "POST",
                                        RouteTemplate = string.IsNullOrWhiteSpace(post.Template) ? $"{routeTemp}/{method.Name}" : $"{routeTemp}/{post.Template}"
                                    };
                                    break;
                                case var a when a is Delete delete:
                                    httpWorkerInfo = new HttpWorkerInfo
                                    {
                                        MethodType = "DELETE",
                                        RouteTemplate = string.IsNullOrWhiteSpace(delete.Template) ? $"{routeTemp}/{method.Name}" : $"{routeTemp}/{delete.Template}"
                                    };
                                    break;
                                case var a when a is Put put:
                                    httpWorkerInfo = new HttpWorkerInfo
                                    {
                                        MethodType = "PUT",
                                        RouteTemplate = string.IsNullOrWhiteSpace(put.Template) ? $"{routeTemp}/{method.Name}" : $"{routeTemp}/{put.Template}"
                                    };
                                    break;
                            }

                            if (httpWorkerInfo != null)
                            {
                                httpWorkerInfo.Method = method;
                                httpWorkerInfo.WorkerType = item;
                                httpWorkerInfo.Instance = instance;

                                RouteTemplate routeTemplate = routeTemplatePatternCreator.Create(httpWorkerInfo.RouteTemplate);
                                routeTemplates.Add(httpWorkerInfo.RouteTemplate, routeTemplate);
                                httpWorkerInfos.Add(httpWorkerInfo);
                            }
                        }
                    }

                }
            }
        }
    }

    public class Route : Attribute
    {
        public Route(string template)
        {
            this.Template = template;
        }
        public string Template { get; }
    }

    public class Post : Attribute
    {
        public Post() { }
        public Post(string template)
        {
            this.Template = template;
        }
        public string Template { get; }
    }

    public class Get : Attribute
    {
        public Get() { }
        public Get(string template)
        {
            this.Template = template;
        }
        public string Template { get; }
    }

    public class Put : Attribute
    {
        public Put() { }
        public Put(string template)
        {
            this.Template = template;
        }
        public string Template { get; }
    }

    public class Delete : Attribute
    {
        public Delete() { }
        public Delete(string template)
        {
            this.Template = template;
        }
        public string Template { get; }
    }

    public class HttpWorkerInfo
    {
        public string MethodType { get; set; }
        public string RouteTemplate { get; set; }
        public Type WorkerType { get; set; }
        public MethodInfo Method { get; set; }
        public object Instance { get; set; }
    }
}
